#include "regex_vm.h"
#include "stack.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

instruction* make_instruction(enum instruction_type type){
    instruction* tmp = (instruction *)malloc(sizeof(instruction));
    tmp->type = type;
    tmp->c=0;
    tmp->op1=NULL;
    tmp->op2=NULL;
    tmp->next = NULL;
    return tmp;
}

regex_thread* make_thread(instruction *op,int pos){
    regex_thread* tmp = (regex_thread *)malloc(sizeof(regex_thread));
    tmp->op= op;
    tmp->pos = pos;
    return tmp;
}

regex_result* make_result(int start,int end){
    regex_result* tmp = (regex_result *)malloc(sizeof(regex_result));
    tmp->start= start;
    tmp->end = end;
    tmp->next = NULL;
    return tmp;
}

void display_result(regex_result *r){
    regex_result *tmp = r;
    if(r == NULL){
        return;
    }
    while(tmp!=NULL){
        printf("start:%d,end:%d\n",tmp->start,tmp->end);
        tmp = tmp->next;
    }

}

regex_result* regex_match(instruction* regex ,char* s,int length){
    int i = 0,m=0;
    int pos = 0;
    Stack *threads = initStack();
    regex_result *result = NULL;
    regex_result *tmp = NULL;
    regex_thread *t=NULL;
    while(pos < length){
        push(threads,make_thread(regex,pos));
        while(stackSize(threads)>0){
            t = (regex_thread *)pop(threads);
            if(t->pos > length){
                break;
            }
            switch(t->op->type){
                case i_char:
                    if(s[t->pos]==t->op->c){
                        //printf("==>%c %c %p\n",s[t->pos],t->op->c,t->op->next );
                        push(threads,make_thread(t->op->next,t->pos+1));
                    }
                    free(t);
                    break;
                case i_word:
                    if((s[t->pos]>='a' && s[t->pos] <='z') || 
                        s[t->pos]>='A' && s[t->pos] <='Z'){
                        push(threads,make_thread(t->op->next,t->pos+1));
                    }
                    free(t);
                    break;
                case i_number:
                    if(s[t->pos]>='0' && s[t->pos] <='9'){
                        push(threads,make_thread(t->op->next,t->pos+1));
                    }
                    free(t);
                    break;
                case i_any:
                    push(threads,make_thread(t->op->next,t->pos+1));
                    free(t);
                    break;
                case i_nonespace:
                if(!(s[t->pos]==' '
                    ||s[t->pos]=='\r'
                    ||s[t->pos]=='\t'
                    ||s[t->pos]=='\n'
                    ||s[t->pos]=='\f'
                    ||s[t->pos]=='\v')){
                        push(threads,make_thread(t->op->next,t->pos+1));
                    }
                    free(t);
                    break;
                case i_space:
                    if(s[t->pos]==' '
                    ||s[t->pos]=='\r'
                    ||s[t->pos]=='\t'
                    ||s[t->pos]=='\n'
                    ||s[t->pos]=='\f'
                    ||s[t->pos]=='\v'){
                        //printf("==>%c %c %p\n",s[t->pos],t->op->c,t->op->next );
                        push(threads,make_thread(t->op->next,t->pos+1));
                    }
                    free(t);
                    break;
                case i_split:
                    push(threads,make_thread(t->op->op2,t->pos ));
                    push(threads,make_thread(t->op->op1,t->pos ));
                    free(t);
                    break;
                case i_jmp:
                    push(threads,make_thread(t->op->op1,t->pos ));
                    free(t);
                    break;
                case i_match:
                    //printf("==>match\n" );
                    if(tmp == NULL){
                        result = make_result(pos,t->pos);
                        tmp = result;
                    }else{
                        tmp->next = make_result(pos,t->pos);
                        tmp = tmp->next;
                    }
                    pos = t->pos - 1;
                    free(t);
                    while(stackSize(threads)>0){
                        t = (regex_thread *)pop(threads);
                        free(t);
                    }
                    break;
                case i_nop:
                    //printf("==>%p\n",t->op->next);
                    push(threads,make_thread(t->op->next,t->pos ));
                    free(t);
                    break;
            }
            
        }
        pos = pos + 1;
    }
    releaseStack(threads);
    return result;
}